package org.btik.light.mobile.ui.serial.controller.console;

import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;

import static java.lang.System.arraycopy;

/**
 * 控制台文本，仅支持单字节字符
 *
 * @author lustre
 * @since 2023/4/14 10:38
 */
public class ConsoleText implements CharSequence {

    private final Line[] values;

    private int firstNode;

    private int endNode;

    private int currentNode;

    private int length;

    public void print(byte[] msg, int length) {
        Line currentLine = values[currentNode];
        for (int i = 0; i < length; i++) {
            byte b = msg[i];
            // 超过宽度换行后才能打印 行数组大小即是它的宽度，可容纳的最大字符数量
            if (currentLine.length == currentLine.lineValue.length) {
                currentLine = nextLine();
            }
            currentLine.write(b);
            if (b == '\n') {
                currentLine = nextLine();
            }
            this.length++;
        }
    }

    private Line nextLine() {
        // 换行
        this.currentNode = endNode;
        Line cunrrentLine = values[currentNode];
        endNode = ++endNode % values.length;

        // 尾节点覆盖到头节点时，头节点出队
        if (endNode == firstNode) {
            Line firstNode = values[this.firstNode];
            this.length -= firstNode.length;
            firstNode.reset();
            this.firstNode = ++this.firstNode % values.length;
        }
        return cunrrentLine;

    }

    @Override
    public int length() {
        return length;
    }

    @Override
    public char charAt(int index) {
        if (index >= length || index < 0) {
            throw new IndexOutOfBoundsException("length:" + length + "index:" + index);
        }

        for (int i = firstNode, searchIndex = 0; i != endNode; i = ++i % values.length) {
            int lineLength = values[i].length;
            int nextSearchIndex = searchIndex + lineLength;
            if (index < nextSearchIndex) {
                return (char) values[i].lineValue[index - searchIndex];
            }
            searchIndex = nextSearchIndex;
        }

        throw new IndexOutOfBoundsException("length:" + length + "index:" + index);
    }

    @NonNull
    @NotNull
    @Override
    public String toString() {
        if (length == 0) {
            return "";
        }
        byte[] bytes = new byte[length];
        int index = 0;
        for (int i = firstNode; i != endNode; i = ++i % values.length) {
            Line line = values[i];
            arraycopy(line.lineValue, 0, bytes, index, line.length);
            index += line.length;
        }
        return new String(bytes);


    }

    @NonNull
    @NotNull
    @Override
    public CharSequence subSequence(int start, int end) {
        return toString().subSequence(start, end);
    }

    public ConsoleText(int maxLine, int maxWidth) {
        if (maxLine < 0 || maxWidth < 0) {
            throw new IndexOutOfBoundsException("line:" + maxLine + " width:" + maxWidth);
        }
        // 一行作为 endNode为空
        maxLine++;
        values = new Line[maxLine];
        for (int i = 0; i < maxLine; i++) {
            values[i] = new Line(maxWidth);
        }
        this.firstNode = 0;
        this.currentNode = 0;
        this.endNode = 1;
    }

    static class Line {
        byte[] lineValue;
        /**
         * 当前行数据的长度
         */
        int length;

        void reset() {
            length = 0;
        }

        void write(byte b) {
            lineValue[length++] = b;
        }

        public Line(int maxWidth) {
            this.lineValue = new byte[maxWidth];
        }
    }
}
